/**
 * Copyright © 2011 Creativor Studio.
 * About license information, please see LICENSE.txt.
 */
package org.creativor.rayson.transport.server;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.creativor.rayson.transport.api.Connection;
import org.creativor.rayson.transport.api.TimeLimitConnection;
import org.creativor.rayson.transport.common.ConnectionProtocol;
import org.creativor.rayson.util.Log;

// TODO: Auto-generated Javadoc
/**
 * This object is used to manage {@link Connection}s in server side of transport
 * layer.
 * 
 * @author Nick Zhang
 */
class ConnectionManager extends Thread
{
	
	private static Logger LOGGER = Log.getLogger();
	private static final int MAX_PENDINGS = 10000;
	
	private static final int THECK_TIME_OUT_INTERVAL = ConnectionProtocol.RPC_TIME_OUT_INTERVAL / 2;
	private ConcurrentHashMap<Long, TimeLimitConnection> connections;
	private HashMap<Long, PendingConnection> pendings;
	private boolean running = true;
	
	/**
	 * Instantiates a new connection manager.
	 */
	ConnectionManager()
	{
		setName("Connection manager");
		connections = new ConcurrentHashMap<Long, TimeLimitConnection>();
		pendings = new HashMap<Long, PendingConnection>();
	}
	
	/**
	 * Accept.
	 * 
	 * @param pendingId
	 *            the pending id
	 * @param connection
	 *            the connection
	 */
	public void accept(long pendingId, TimeLimitConnection connection)
	{
		// throw new DenyServiceException();
		this.pendings.remove(pendingId);
		this.connections.put(connection.getId(), connection);
	}
	
	/**
	 * Accept pending.
	 * 
	 * @param connection
	 *            the connection
	 * @throws DenyServiceException
	 *             the deny service exception
	 */
	public void acceptPending(PendingConnection connection) throws DenyServiceException
	{
		if (this.pendings.size() > MAX_PENDINGS) throw new DenyServiceException();
		this.pendings.put(connection.getId(), connection);
	}
	
	private void checkTimeouts()
	{
		for (Iterator<TimeLimitConnection> iterator = this.connections.values().iterator(); iterator
				.hasNext();)
		{
			TimeLimitConnection conn = iterator.next();
			if (conn.isTimeOut()) try
			{
				LOGGER.info("Remove and close time out conection: " + conn.toString());
				iterator.remove();
				conn.close();
			} catch (IOException e)
			{
				LOGGER.log(Level.SEVERE, "Close time out connection error: " + e.getMessage());
			}
			
		}
	}
	
	/**
	 * Removes the.
	 * 
	 * @param connection
	 *            the connection
	 */
	public void remove(Connection connection)
	{
		this.connections.remove(connection.getId());
	}
	
	/**
	 * Removes the pending.
	 * 
	 * @param connection
	 *            the connection
	 */
	public void removePending(PendingConnection connection)
	{
		this.pendings.remove(connection.getId());
	}
	
	/**
	 * Quit this connection manager thread.
	 */
	public void quit()
	{
		this.running = false;
	}
	
	/**
	 * Run.
	 */
	@Override
	public void run()
	{
		LOGGER.info(getName() + " starting...");
		while (running)
		{
			try
			{
				Thread.sleep(THECK_TIME_OUT_INTERVAL);
			} catch (InterruptedException e)
			{
				break;
			}
			checkTimeouts();
		}
		
		LOGGER.info(getName() + " stoped");
		
	}
	
	/**
	 * Size.
	 * 
	 * @return the int
	 */
	public int size()
	{
		return this.connections.size();
	}
}
